//
//  ChatsController.m
//  SolarSeek
//
//  Created by Erick Fejta on 05/05/21.
//  Copyright 2005 __MyCompanyName__. All rights reserved.
//

#import "ChatsController.h"
#import "PeersController.h"
#import "Message.h"
#import "Chats.h"
#import "Connections.h"
#import "ServerConnectionController.h"


@interface ChatsController (Private)
- (void)releaseRoom:(Room *)room;
- (void)releaseChat:(Chat *)chat;
- (void)processServerMessage:(Message *)message;
- (void)addStatement:(NSString *)statement fromPeer:(Peer *)peer toChat:(Chat *)chat;
@end

@interface ControlledPrivateChat : PrivateChat {}
@end

@interface ControlledRoomChat : RoomChat {}
@end

@interface ControlledRoom : Room
@end

@implementation ControlledRoomChat
- (id)valueForUndefinedKey:(NSString *)key {
	NSLog(key);
	return nil;
}
- (void)release {
	if([self retainCount] == 1 ) {
		[super release];
//		[[[NSApp engine] chatsController] releaseChat:self];
	}
	else
		[super release];
}

- (void)dealloc {
//	NSLog(@"D'oh");
	[super dealloc];
}

@end

@implementation ControlledPrivateChat
- (void)release {
	if([self retainCount] == 1 ) {
		[super release];
//		[[[NSApp engine] chatsController] releaseChat:self];
	}
	else
		[super release];
}
@end


@implementation ControlledRoom 
- (void)dealloc {
//	NSLog(@"D'oh room");
	[super dealloc];
}
- (void)release {
	if([self retainCount] == 1 ) {
		[super release];
//		[[[NSApp engine] chatsController] releaseRoom:self];
	}
	else
		[super release];
}
@end

#pragma mark -


typedef enum { ObserveNone=0,ObserveServerMessage,ObserveLogIn } ObservingMessages;

@implementation ChatsController

- (id)initWithEngine:(SolarSeekEngine *)engine {
	if(self = [super init]) {
		[engine addObserver:self forKeyPath:@"serverConnection.messageReceived" options:nil context:(void *)ObserveServerMessage];
		[engine addObserver:self forKeyPath:@"serverConnectionController.loggedIn" options:nil context:(void *)ObserveLogIn];
		
		_chatsRoom = [[KVMutableSet alloc] initWithDefaultDelay:0.05];
		_chatsPrivate = [[KVMutableSet alloc] initWithDefaultDelay:0.05];
		_chats = [[KVMutableSet alloc] initWithDefaultDelay:0.05];
		
		_rooms = [[KVMutableSet alloc] initWithDefaultDelay:0.05];
		
		_chatsJoined = [[KVMutableSet alloc] init];
		_chatsJoining = [[KVMutableSet alloc] init];
		_chatsLeft = [[KVMutableSet alloc] init];
		_chatsInside = [[KVMutableSet alloc] initWithDefaultDelay:0.0];
		_chatsList = [[KVMutableSet alloc] initWithDefaultDelay:0.05];
		
		[self addRoomToList:[self roomNamed:@"Mac Users"]];
		[self addRoomToList:[self roomNamed:@"SolarSeek"]];
	}
	return self;
}

- (void)dealloc {
	[[NSApp engine] removeObserver:self forKeyPath:@"serverConnection.messageReceived"];
	[[NSApp engine] removeObserver:self forKeyPath:@"serverConnectionController.loggedIn"];
	[_chatsRoom release];
	[_chatsPrivate release];
	[_chats release];
	
	[_rooms release];
	
	[_chatsJoined release];
	[_chatsJoining release];
	[_chatsLeft release];
	[_chatsInside release];
	[_chatsList release];
	[super dealloc];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
	ObservingMessages kind = (ObservingMessages)context;
	switch(kind) {
		case ObserveServerMessage: {
			Message *m = [[[NSApp engine] serverConnection] messageReceived];
			if(m)
				[self processServerMessage:m];
			break;
		}
		case ObserveLogIn: {
			if([[[NSApp engine] serverConnectionController] loggedIn]==NO) {
				NSEnumerator *e = [[_chatsJoined mutableSet] objectEnumerator];
				Chat *c;
				while(c = [e nextObject]) {
					if([c type]==ChatTypeRoom)
						[[[c peersJoined] mutableSet] removeAllObjects];
				}
				e = [[_rooms mutableSet] objectEnumerator];
				Room *r;
				while(r = [e nextObject]) {
					[r setJoined:NO];
				}
				[[_chatsLeft mutableSet] unionSet:[_chatsJoined mutableSet]];
				[[_chatsJoined mutableSet] removeAllObjects];
				[[_chatsJoining mutableSet] removeAllObjects];
			}
			else {
				NSEnumerator *e = [[_chatsInside mutableSet] objectEnumerator];
				Chat *c;
				while(c = [e nextObject]) {
					if([c type]==ChatTypeRoom)
						[self chatWithRoom:[c room] autojoin:YES];
				}
			}
			break;
		}
	}
}

- (void)releaseRoom:(Room *)room { [[_rooms mutableSet] removeObject:room allowDelay:0.0]; }
- (void)releaseChat:(Chat *)chat { 
	[[_chatsPrivate mutableSet] removeObject:chat allowDelay:0.0]; 
	[[_chatsRoom mutableSet] removeObject:chat allowDelay:0.0];
	[[_chats mutableSet] removeObject:chat allowDelay:0.0];
}


- (KVMutableSet *)chatsAll { return _chats; }
- (KVMutableSet *)chatsRoom { return _chatsRoom; }
- (KVMutableSet *)chatsPrivate { return _chatsPrivate; }
- (KVMutableSet *)chatsList { return _chatsList; }
- (KVMutableSet *)chatsInside { return _chatsInside; }
- (KVMutableSet *)chatsJoined { return _chatsJoined; }
- (KVMutableSet *)rooms { return _rooms; }

- (Chat *)chatWithChat:(Chat *)chat {
	Chat *rVal = [[_chats mutableSet] member:chat];
	if(rVal == nil) {
		switch([chat type]) {
			case ChatTypeRoom: {
				rVal = [[ControlledRoomChat alloc] initWithRoom:[chat room]];
				[[_chatsRoom mutableSet] addObject:rVal];
				break;
			}
			case ChatTypePrivate: {
				rVal = [[ControlledPrivateChat alloc] initWithPeer:[chat peer]];
				[[_chatsPrivate mutableSet] addObject:rVal];
				break;
			}
			default:
				[NSException raise:@"UnknownChatType" format:@"The chat type %d is invalid.",[chat type]];
		}
		[[_chats mutableSet] addObject:rVal];
		[rVal autorelease];
	}
	return rVal;
}

- (void)joinRoomChat:(RoomChat *)chat {
	if([[_chatsJoined mutableSet] containsObject:chat]==NO &&
		[[_chatsJoining mutableSet] containsObject:chat]==NO) {
		[[_chatsInside mutableSet] addObject:chat];
		[[chat room] setInside:YES];
		[NSObject cancelPreviousPerformRequestsWithTarget:[_chatsJoining mutableSet] selector:@selector(removeObject:) object:chat];
		[[_chatsJoining mutableSet] addObject:chat];
		[[_chatsLeft mutableSet] removeObject:chat];
		[[_chatsJoining mutableSet] performSelector:@selector(removeObject:) withObject:chat afterDelay:50.0];
		Message *m = [Message messageWithCode:JoinRoom];
		[m putString:[[chat room] name]];
		[[NSApp engine] sendServerMessage:m];
	}
}

- (RoomChat *)chatWithRoom:(Room *)room autojoin:(BOOL)join {
	RoomChat *rc = [RoomChat chatWithRoom:room];
	rc = (RoomChat *)[self chatWithChat:rc];
	if(join) {
		[self joinRoomChat:rc];
	}
	return rc;
}
		
	
- (RoomChat *)chatWithRoom:(Room *)room { 
	RoomChat *rc = [self chatWithRoom:room autojoin:YES];
	[[_chatsList mutableSet] addObject:rc];
	return rc;
}

- (PrivateChat *)chatWithPeer:(Peer *)peer {
	PrivateChat *pc = (PrivateChat *)[self chatWithChat:[PrivateChat chatWithPeer:peer]];
	[[_chatsInside mutableSet] addObject:pc];
//DEL	[[_chatsList mutableSet] addObject:pc];
	return pc;
}

- (Room *)roomNamed:(NSString *)room { 
	Room *r = [Room roomNamed:room];
	Room *r2 = [[_rooms mutableSet] member:r];
	if(r2 == nil) {
		r2 = [[ControlledRoom alloc] initWithName:room];
		[[_rooms mutableSet] addObject:r2];
		[r2 autorelease];
	}
	return r2;
}

- (void)say:(NSString *)statement inChat:(Chat *)chat {
	Message *m=nil;
	switch([chat type]) {
		case ChatTypeRoom: {
//			NSLog(@"CHAT (room): said %@ to %@",statement,[[chat room] name]);
			m = [Message messageWithCode:SayChatroom];
			[m putString:[[chat room] name]];
			break;
		}
		case ChatTypePrivate: {
//			NSLog(@"CHAT (private): said %@ to %@",statement,[[chat peer] name]);
			m = [Message messageWithCode:MessageUser];
			[m putString:[[chat peer] name]];
			break;
		}
		default:
			[NSException raise:@"UnknownChatType" format:@"The type %d is not a valid chat type.",[chat type]];
	}
	[m putString:statement];
	[[NSApp engine] sendServerMessage:m];
	[self addStatement:statement fromPeer:[[NSApp engine] me] toChat:chat];
}

- (void)say:(NSString *)statement toPeer:(Peer *)peer {
	[self say:statement inChat:[self chatWithChat:[PrivateChat chatWithPeer:peer]]];
}

- (void)say:(NSString *)statement toRoom:(Room *)room {
	[self say:statement inChat:[self chatWithChat:[RoomChat chatWithRoom:room]]];
}

- (void)endChat:(Chat *)chat {	
	if([chat type]==ChatTypeRoom && [[chat room] inside]==YES) {
		Message *m = [Message messageWithCode:LeaveRoom];
		[m putString:[[chat room] name]];
		[[NSApp engine] sendServerMessage:m];
		[[chat room] setInside:NO];
		[[_chatsInside mutableSet] removeObject:chat allowDelay:0.0];
	}
	else if([chat type]==ChatTypePrivate) {
//DEL		[[_chatsList mutableSet] removeObject:chat];
		[[_chatsInside mutableSet] removeObject:chat];
	}
	[[_chatsJoined mutableSet] removeObject:chat];
	[[_chatsJoining mutableSet] removeObject:chat];
	[[_chatsLeft mutableSet] addObject:chat];
}

- (void)joinRoomWithMessage:(Message *)message {
	NSString *roomname = [message getString];
	Room *room = [self roomNamed:roomname];
	RoomChat *chat = [self chatWithRoom:room autojoin:NO];
	[chat joinPeer:[[NSApp engine] me]];
	[room setInside:YES];
	[room setJoined:YES];
	[[_chatsInside mutableSet] addObject:chat];
	[[_chatsJoined mutableSet] addObject:chat];
	[[_chatsJoining mutableSet] removeObject:chat];
	[[_chatsLeft mutableSet] removeObject:chat];
	[NSObject cancelPreviousPerformRequestsWithTarget:[_chatsJoining mutableSet] selector:@selector(removeObject:) object:chat];
	
	unsigned people = [message getInt];
	int i;
	for(i=0;i<people;i++) {
		NSString *username = [message getString];
		Peer *peer = [[NSApp engine] peerNamed:username];
		[chat joinPeer:peer];
	}
}

- (void)leaveRoomWithMessage:(Message *)message {
	NSString *name = [message getString];
	Room *room = [self roomNamed:name];
	[room setJoined:NO];
	RoomChat *chat = [self chatWithRoom:room autojoin:NO];
	[[[chat peersJoined] mutableSet] removeAllObjects];
	[chat partPeer:[[NSApp engine] me]];
	[[_chatsLeft mutableSet] addObject:chat];
	[[_chatsJoining mutableSet] removeObject:chat];
	[[_chatsJoined mutableSet] removeObject:chat];
	
}


- (void)changePeersInRoomFromMessage:(Message *)message joining:(BOOL)joining {
	NSString *name = [message getString];
	NSString *username = [message getString];
	Peer *peer = [[NSApp engine] peerNamed:username];
	Room *room = [self roomNamed:name];
	RoomChat *chat = [self chatWithRoom:room autojoin:NO];
//	NSLog(@"CHAT: %@ is %@ (%d).",peer,joining?@"joining":@"leaving",[[message data] length]);
	if(joining) {
		if([peer availability] == AvailabilityUnknown)
			[[NSApp engine] updateStatusPeer:peer];
		[chat joinPeer:peer];
	}
	else
		[chat partPeer:peer];
}

- (void)addStatement:(NSString *)statement fromPeer:(Peer *)peer toChat:(Chat *)chat {
//	NSLog(@"CHAT: %@ said %@ in %@.",peer,statement,chat);
	[peer addPrivileges:PeerPrivilegesTalked];
	[chat addStatement:statement fromPeer:peer];
}

- (void)addRoomStatementFromMessage:(Message *)message {
	NSString *roomname = [message getString],
			 *username = [message getString],
			 *statement = [message getString];
	Room *room = [self roomNamed:roomname];
	Peer *peer = [[NSApp engine] peerNamed:username];
	if([peer isEqual:[[NSApp engine] me]]==NO) {
		RoomChat *chat = [self chatWithRoom:room autojoin:NO];
		[self addStatement:statement fromPeer:peer toChat:chat];
	}
}

- (void)addPrivateStatementFromMessage:(Message *)message {
	unsigned messageId = [message getInt],
			timestamp = [message getInt];
	NSString *username = [message getString],
			 *statement = [message getString];
	
	Peer *peer = [[NSApp engine] peerNamed:username];
	PrivateChat *chat = [self chatWithPeer:peer];
//DEL	[[_chatsList mutableSet] addObject:chat];
	[self addStatement:statement fromPeer:peer toChat:chat];
	if(messageId) {
		Message *m = [Message messageWithCode:MessageAcked];
		[m putInt:messageId];
		[[NSApp engine] sendServerMessage:m];
	}
}

- (void)addRoomsToList:(NSArray *)rooms {
	NSEnumerator *e = [rooms objectEnumerator];
	Room *r;
	while(r = [e nextObject]) {
		RoomChat *rc = [self chatWithRoom:r autojoin:NO];
		[[_chatsList mutableSet] addObject:rc];
	}
}

- (void)removeRoomsFromList:(NSArray *)rooms {
	NSEnumerator *e = [rooms objectEnumerator];
	Room *r;
	while(r = [e nextObject]) {
		RoomChat *rc = [self chatWithRoom:r autojoin:NO];
		[[_chatsList mutableSet] removeObject:rc];
	}
}


- (void)addRoomToList:(Room *)room { [self addRoomsToList:[NSArray arrayWithObject:room]]; }
- (void)removeRoomFromList:(Room *)room { [self removeRoomsFromList:[NSArray arrayWithObject:room]]; }


- (void)updateRoomListFromMessage:(Message *)message {
	unsigned count = [message getInt];
	int i;
	NSMutableArray *a = [NSMutableArray arrayWithCapacity:count];
	for(i=0;i<count;i++) {
		Room *r = [self roomNamed:[message getString]];
		[a addObject:r];
	}
	count = [message getInt];
	for(i=0;i<count;i++) {
		Room *r = [a objectAtIndex:i];
		[r setCountOfPeers:[message getInt]];
	}
	[self addRoomsToList:a];
}
	

- (void)processServerMessage:(Message *)message {
	[message rewind];
	int code = [message getCode];
	NS_DURING
		switch(code) {
			case JoinRoom:
				[self joinRoomWithMessage:message];
				break;
			case LeaveRoom: 
				[self leaveRoomWithMessage:message];
				break;
			case UserJoinedRoom:
				[self changePeersInRoomFromMessage:message joining:YES];
				break;
			case UserLeftRoom:
				[self changePeersInRoomFromMessage:message joining:NO];
				break;
			case SayChatroom:
				[self addRoomStatementFromMessage:message];
				break;
			case MessageUser:
				[self addPrivateStatementFromMessage:message];
				break;
			case RoomList:
				[self updateRoomListFromMessage:message];
				break;
		}
	NS_HANDLER
		NSLog(@"Encountered an exception while the Chat Controller was parsing a server message with code %d: %@",code,localException);
		NSLog(@"Ignoring exception and continuting...");
	NS_ENDHANDLER
}

@end
